# -*-coding=UTF-8-*-
import urllib2
import urllib
import socket
import cookielib
import simplejson
import time
import hashlib
import random
from base64 import encodestring
from types import StringType, NoneType
from utils.tools import md5, getLogger

"""
Class Orchid -- Wrappered for sending RESTfull API request
get(api, params, **kwargs)
put(api, data, **kwargs)
post(api, data, **kwargs)
delete(api, params, **kwargs)
options(api)
"""


class Orchid(object):
    i_headers = {
        "User-Agent": "epic/1.0",
        "Accept": "application/json",
        "Content-Type": "application/json;charset=UTF-8"
    }

    timeout = 60

    def __init__(self, auth_type=None, protocol='https', prefix='/api', host='127.0.0.1', port=443, logger=None):
        # Response
        self.res = None
        # Request
        self.req = None
        # Auth method
        self.auth_type = auth_type
        # protocol
        self.protocol = protocol
        # prefix
        self.prefix = prefix
        # host
        self.host = host
        # port
        self.port = port
        # Target Api
        self.api = None
        # Target url
        self.url = None
        # HTTP Method ['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS']
        self.method = "GET"
        # HTTP query parameters
        self.query = None
        # HTTP request data
        self.body = None
        # Response content
        self.result = None
        # Http Basic Auth Info
        self.basic_info = {"pwd": "46ef573ab54c88e39b842418b19208ee", "user": "hehehaha"}
        # APP ID
        self.appId = None
        # Access Token
        self.accessToken = None
        # HTTP request content-type
        self.content = "application/json; charset=UTF-8"
        # Cookie
        self.cookies = []
        if logger is None:
            self.logger = getLogger("orchid")
        else:
            self.logger = logger

    def _initRequest(self, api, query=None, data=None):
        self.api = api
        self.body = data
        self.query = query

        if self.auth_type:
            if self.auth_type == 'signature':
                self._signature()
            elif self.auth_type == 'basic':
                self._basic()
            else:
                pass

        if self.query:
            self.url = "%s://%s:%s%s%s?%s" % (self.protocol, self.host, self.port, self.prefix, self.api, urllib.urlencode(self.query))
        else:
            self.url = "%s://%s:%s%s%s" % (self.protocol, self.host, self.port, self.prefix, self.api)
        self.req = urllib2.Request(self.url, headers=self.i_headers)

    def _basic(self):
        base_str = encodestring('%s:%s' % (self.basic_info['user'], self.basic_info['pwd']))[:-1]
        self.i_headers['Authorization'] = "Basic %s" % base_str

    def set_cookie(self, cookies):
        cookies_arr = []
        for (cookie_name, cookie_value) in cookies.items():
            cookies_arr.append("%s=%s" % (cookie_name, cookie_value))
        self.i_headers['Cookie'] = ';'.join(cookies_arr)

    def setTarget(self, proto=None, host=None, port=None):
        self.protocol = proto if proto else self.protocol
        self.host = host if host else self.host
        self.port = port if port else self.port

    def _signature(self):
        if self.api.startswith('/api'):
            raw_str1 = self.api[4:]
        else:
            raw_str1 = self.api
        hash_str1 = md5(raw_str1)
        new_query = dict()
        if self.query:
            for key, value in self.query.items():
                if isinstance(value, str) == False:
                    new_query[key] = str(value)
                else:
                    new_query[key] = value
        hash_str2 = md5(simplejson.dumps(new_query, sort_keys=True, separators=(',', ':')) if new_query else "")
        #hash_str2 = md5(simplejson.dumps(self.query) if self.query else "")
        hash_str3 = md5(simplejson.dumps(self.body) if self.body else "")
        signList = {
            'timestamp': str(int(time.mktime(time.localtime()))),
            'nonce': str(random.random()),
            # 'appId': str(self.appId),
            'devHash': str(self.appId),
            'token': self.accessToken,
            'hash_str1': hash_str1,
            'hash_str2': hash_str2,
            'hash_str3': hash_str3
        }
        signs = signList.values()
        signs.sort()
        signature = hashlib.sha1(''.join(signs)).hexdigest()
        # self.logger.info("singList %s" % signList)
        # self.logger.info("signature %s" % signature)
        query = self.query if self.query else {}
        # query["appId"] = signList['appId']
        query["devHash"] = signList['devHash']
        query["nonce"] = signList['nonce']
        query["timestamp"] = signList['timestamp']
        query["signature"] = signature
        self.query = query

    def setdevHash(self, hash):
        self.appId = hash

    def setProto(self, protocol):
        self.protocol = protocol

    def setHost(self, host):
        self.host = host

    def setPort(self, port):
        self.port = port

    def setPrefix(self, prefix):
        self.prefix = prefix

    def setAuthType(self, auth_type):
        self.auth_type = auth_type

    def setBasicInfo(self, basic_info):
        self.basic_info = basic_info

    def disableAuth(self):
        self.setAuthType(None)

    def enableAuth(self):
        self.setAuthType(True)

    def getCode(self):
        code = None
        if self.res:
            code = self.res.getcode()
        return code

    def getCookies(self):
        return self.cookies

    def setHeader(self, i_headers):
        self.i_headers = i_headers

    def getError(self):
        return {
            "code": self.code,
            "reason": self.reason,
            "result": self.result
        }

    def get_headers(self):
        return self.res.headers

    def _reset(self):
        self.api = None
        self.res = None
        self.query = None
        self.body = None
        self.code = None
        self.reason = None
        self.result = None
        self.cookies = []

    def getResult(self):
        if type(self.result) is NoneType:
            if "OPTIONS" == self.method:
                self.result = self.res.headers.get("Allow").split(",")
            else:
                if self.res:
                    result = self.res.read()
                    try:
                        self.result = simplejson.loads(result)
                    except Exception, e:
                        print str(e)
                        self.result = result
                else:
                    self.result = None
        else:
            try:
                if type(self.result) is StringType:
                    self.result = simplejson.loads(self.result)
            except Exception, e:
                print str(e)

        ret = self.result
        if type(self.result) is NoneType or type(self.result) is StringType:
            ret = {
                "errCode": 17025,
                "errMsg": "Unexpected Response From Target API",
                "response": self.result
            }
        return ret

    def _sendRequest(self):
        try:
            # self.logger.info("%s %s" % (self.method, self.url))
            if self.method in ["POST", "PUT"]:
                data = simplejson.dumps(self.body)
                # self.logger.info("request data: \n %s" % data)
                self.res = urllib2.urlopen(
                    self.req,
                    data=data,
                    timeout=self.timeout
                )
            else:
                cookie = cookielib.CookieJar()
                opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
                self.res = opener.open(self.req, timeout=self.timeout)
                self.cookies = cookie
                # self.res = urllib2.urlopen(self.req, timeout=self.timeout)
            self.code = self.res.getcode()
            self.logger.debug("Orchid: %s - %s - %s - %d" % (self.method, self.url, self.getResult(), self.code))
        except urllib2.HTTPError, e:
            self.logger.warn("Fail on %s: %s - %s" % (self.method, self.url, str(e)))
            self.code = e.code
            self.reason = e.reason
            self.result = e.read()

            if 401 == e.code:
                class Http401(Exception):
                    status = 401
                    pass

                raise Http401
        except urllib2.URLError, e:
            if hasattr(e, "reason"):
                self.reason = e.reason.strerror
            elif hasattr(e, "code"):
                self.code = e.code
                self.result = e.read()

            if isinstance(self.res, socket.timeout):
                self.reason = "Timeout On Waiting For Response"
            elif isinstance(self.res, socket.error):
                self.reason = "Fail On Connect To Target Server"

            self.logger.warn("Fail on %s: %s - %s" % (self.method, self.url, str(e)))
        except Exception, e:
            self.reason = "Unknow Error"
            self.logger.warn("Fail on %s: %s - %s" % (self.method, self.url, str(e)))

    def _callApiRequest(self, method, api, query=None, data=None):
        self._reset()
        ret = False
        if api:
            self.method = method
            self._initRequest(api, query, data)

            if method in ['DELETE', 'PUT', 'OPTIONS']:
                self.req.get_method = lambda: method

            self._sendRequest()

            if self.code == 200:
                ret = True
        return ret

    def get(self, api, query=None):
        return self._callApiRequest("GET", api, query)

    def post(self, api, data, query=None):
        return self._callApiRequest("POST", api, query, data)

    def delete(self, api, query=None):
        return self._callApiRequest("DELETE", api, query)

    def put(self, api, data, query=None):
        return self._callApiRequest("PUT", api, query, data)

    def options(self, api):
        return self._callApiRequest("OPTIONS", api)

    def set_real_ip(self, request):
        user_ip = self.host
        if "HTTP_X_FORWARDED_FOR" in request.META.keys():
            user_ip = request.META.get("HTTP_X_FORWARDED_FOR")
        else:
            user_ip = request.META.get("REMOTE_ADDR")
        self.i_headers['X-Forwarded-For'] = user_ip

if __name__ == "__main__":
    orchid = Orchid()
    print "=" * 20
    orchid.setHost("127.0.0.1")
    orchid.setAuthType('signature')
    orchid.appId = "ABCD-EFGH-IJKL-MNOP"
    orchid.accessToken = "5e95c63d-c961-4afa-856e-75be16353c7a"
    if orchid.post("/v1/devices/msg", [{"hash": "ABCD-EFGH-IJKL-MNOP", "product": "COLLECTOR", "msgtype": 71000},
                                       {"timeStamp": 1467657607000, "devIp": "10.65.120.20"}]):
        print "Success on Request"
        print orchid.getResult()
    else:
        print "Fail on Request"
        print orchid.getError()